package com.ruoyi.shop.modules.service.impl;

import com.github.javafaker.Faker;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.shop.common.utils.CancelOrderSender;
import com.ruoyi.shop.common.utils.SnowflakeIdWorker;
import com.ruoyi.shop.modules.domain.Order;
import com.ruoyi.shop.modules.domain.Product;
import com.ruoyi.shop.modules.mapper.OrderMapper;
import com.ruoyi.shop.modules.mapper.ProductMapper;
import com.ruoyi.shop.modules.service.IOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static com.ruoyi.shop.common.constant.Constants.*;

/**
 * 订单Service业务层处理
 *
 * @author zjc
 * @date 2024-06-26
 */
@Service
@Slf4j
public class OrderServiceImpl implements IOrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private CancelOrderSender cancelOrderSender;
    private static Faker faker = new Faker();
    /**
     * 查询订单
     *
     * @param id 订单主键
     * @return 订单
     */
    @Override

    public Order selectOrderById(Long id) {
        return orderMapper.selectOrderById(id);
    }

    /**
     * 查询订单列表
     *
     * @param order 订单
     * @return 订单
     */
    @Override
    public List<Order> selectOrderList(Order order) {
        return orderMapper.selectOrderList(order);
    }

    /**
     * 新增订单
     *
     * @param order 订单
     * @return 结果
     */
    @Override
    public Order insertOrder(Order order) {
        order.setCreateTime(DateUtils.getNowDate());
        //todo 执行一系类下单操作
        log.info("生成订单：{}", order.getOrderSn());
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1, 1);
        order.setOrderSn(faker.bothify("????-####-###"));
        order.setDeliverySn(faker.bothify("D###"));
        order.setDeliveryCompany(faker.company().name());
        order.setReceiverDetailAddress(faker.address().fullAddress());
        order.setStatus(0L);
        int result = orderMapper.insertOrder(order);

        //下单完成后开启一个延迟消息，用于当用户没有付款时取消订单（orderId应该在下单后生成）
        sendDelayMessageCancelOrder(order.getId());
        return order;
    }


    /**
     * 发送刚提交的订单id至延时队列
     * @param orderId
     */
    private void sendDelayMessageCancelOrder(Long orderId) {
        //获取订单超时时间，假设为3秒
        long delayTimes = ORDER_EXPIRE; //ms为单位
        //发送延迟消息
        cancelOrderSender.sendMessage(orderId, delayTimes);
    }

    /**
     * 修改订单
     *
     * @param order 订单
     * @return 结果
     */
    @Override
    public int updateOrder(Order order) {
        order.setStatus(2L);
        return orderMapper.updateOrder(order);
    }

    /**
     * 批量删除订单
     *
     * @param ids 需要删除的订单主键
     * @return 结果
     */
    @Override
    public int deleteOrderByIds(Long[] ids) {
        return orderMapper.deleteOrderByIds(ids);
    }

    /**
     * 删除订单信息
     * 删除订单有两种情况：
     * 1. 订单超时未支付，则直接删除
     * 2. 用户不要订单，手动点击删除
     * @param id 订单主键
     * @return 结果
     */
    @Override
    public int deleteOrderById(Long id) {
        // 1判断订单的
        Order order = orderMapper.selectOrderById(id);
        if(null == order){
            return -1;
        }
        if (order.getStatus() != 0){
            return -1;
        }
        return orderMapper.deleteOrderById(id);
    }


    /**
     * 聚合每个用户购买过的的商品id,并写入redis SET
     */
    @Override
    public void setUPMapToRedis() {
        // 第一步：获取所有订单并构建用户ID与产品ID的映射
        Map<Long, Set<Long>> memberIdToProductIdsMap = buildMemberIdToProductIdsMap();

        // 第二步：将映射写入Redis的SET中
        writeToRedisSet(memberIdToProductIdsMap);
    }

    /**
     * 构建用户id与商品ids的映射
     *
     * @return 用户id与商品ids的映射
     */
    @Override
    public Map<Long, Set<Long>> buildMemberIdToProductIdsMap() {
        // 先查出所有订单
        List<Order> orders = orderMapper.selectOrderList(new Order());

        // 使用Map来聚合订单中的商品id
        Map<Long, Set<Long>> map = new HashMap<>();
        orders.forEach(order -> {
            Long memberId = order.getMemberId();
            Long productId = order.getProductId();

            // 如果map中不存在该用户id，则创建一个新的set用于存储该用户的商品id
            map.computeIfAbsent(memberId, k -> new HashSet<>()).add(productId);
        });

        return map;
    }

    private void writeToRedisSet(Map<Long, Set<Long>> memberIdToProductIdsMap) {
        // 遍历映射并将商品id集合写入redis set
        memberIdToProductIdsMap.forEach((memberId, productIds) -> {
            System.out.println("memberId:" + memberId);
            productIds.forEach(System.out::print);
            System.out.println();
            // 将商品id集合写入redis set
            redisCache.setCacheSet(UP_SET_PREFIX + memberId, productIds);
        });
    }


    @Override
    public List<Product> getGuessLikeProducts(Long userId) {
        //查找有没有redis推荐的商品
        Set<Long> recommendProducts = redisCache.getCacheSet(RECOMMEND_UP_PREFIX + userId);
        //如果redis有推荐商品，则直接返回
        if (recommendProducts != null && !recommendProducts.isEmpty()) {
            return productMapper.selectProductByIds(recommendProducts.toArray(new Long[0]));
        }
        //如果redis没有推荐商品，则开始计算
        //获取用户购买过的商品id集合
        Set<Long> userProductIds = redisCache.getCacheSet(UP_SET_PREFIX + userId);
        //如果没有获取商品，先计算一次
        setUPMapToRedis();
        //再获取用户购买过的商品id集合
        userProductIds = redisCache.getCacheSet(UP_SET_PREFIX + userId);
        //还没有，说明没有购买过商品，直接返回null
        if (userProductIds == null) {
            return new ArrayList<>();
        }
        //开始商品推荐
        //获取redis中所有用户id
        Collection<String> memberKeys = redisCache.keys(UP_SET_PREFIX + "*");

        Set<Long> recommendProductIds = new HashSet<>();
        //遍历所有用户id
        for (String memberKey : memberKeys) {
            //如果当前用户id是目标用户id，则跳过
            if (memberKey.equals(UP_SET_PREFIX + userId)) {
                continue;
            }
            //
            String memberId = memberKey.split(":")[2];
            try {
                //对用户进行商品推荐
                recommendProductIds.addAll(getRecommendForUser(userId, Long.valueOf(memberId)));
            } catch (Exception e) {
                log.error("推荐商品失败，userId:{}", userId);
            }
        }
        if (recommendProductIds.isEmpty()) {
            return new ArrayList<>();
        }
        //改List
        return productMapper.selectProductByIds(recommendProductIds.toArray(new Long[0]));

    }

    /**
     * 为用户1推荐用户2购买过的其他商品，但前提是用户2跟用户
     *
     * @param userId1 目标用户id
     * @param userId2 对比用户id
     * @return 对比用户的不同商品ID集合，如果无法计算或相似度不够则返回null
     */
    public Set<Long> getRecommendForUser(Long userId1, Long userId2) {
        // 获取商品总数
        long totalProductCount = getTotalProductCount();
        // 获取两个用户的商品集合
        String setKey1 = UP_SET_PREFIX + userId1;
        String setKey2 = UP_SET_PREFIX + userId2;
        Set<Long> productIds1 = redisCache.getCacheSet(setKey1);
        Set<Long> productIds2 = redisCache.getCacheSet(setKey2);
        // 获取商品总数
        long productCount1 = productIds1.size();
        long productCount2 = productIds2.size();
        // 异或操作，得到用户2购买过而用户1没买过的商品
        Set<Long> xorSet = productIds2.stream().filter(productId -> !productIds1.contains(productId)).collect(Collectors.toSet());
        // 计算不同商品数
        long different = xorSet.size();
        // 计算相似度
        double similarity = calculateSimilarity(totalProductCount, different, productCount1, productCount2);
        System.out.println(similarity);
        if (similarity < 0.7) {
            return null; // 如果相似度不够，则返回null
        }
        // 先删除以前的推荐结果
//        redisCache.deleteObject(RECOMMEND_UP_PREFIX + userId1);
        // 将异或结果写入redis
        redisCache.setCacheSet(RECOMMEND_UP_PREFIX + userId1, xorSet);
        // 返回异或操作后的结果
        return xorSet;
    }


    /**
     * 获取商品总数
     *
     * @return 商品总数，如果无法获取则返回null
     */
    private Long getTotalProductCount() {
        // 从Redis缓存中获取商品总数
        Long totalProductCount = redisCache.getCacheObject(TOTAL_PRODUCT_COUNT);
        if (totalProductCount == null) {
            // 如果缓存中不存在商品总数，则从数据库中获取
            totalProductCount = productMapper.getProductCount();
            // 将获取到的商品总数存入Redis缓存
            redisCache.setCacheObject(TOTAL_PRODUCT_COUNT, totalProductCount);
        }
        // 返回商品总数
        return totalProductCount;
    }


    /**
     * 计算余弦相似度
     *
     * @param totalProductCount 商品总数
     * @param different         不同商品数
     * @param total1            用户1的商品总数
     * @param total2            用户2的商品总数
     * @return 相似度
     */
    private double calculateSimilarity(long totalProductCount, long different, long total1, long total2) {
        //共有商品数
        long commons = totalProductCount - different;
        // 计算余弦相似度
        return commons / Math.sqrt((totalProductCount + total1) * (totalProductCount + total2));
    }

    @Override
    public List<Order> selectUserOrder(Long userId) {
        return orderMapper.selectUserOrder(userId);
    }

    @Override
    public List<Order> selectOrderByIds(Long[] ids) {
        return orderMapper.selectOrderByIds(ids);
    }
}
